export class Browser {
    #option: Option;
    #DevToolsActivePortFile: string;
    #browserProcess!: Deno.Process;
    #ws?: WebSocket;

    public get ws(): WebSocket | undefined {
        return this.#ws;
    }

    private constructor(browserProcess: Deno.Process, option: Option) {
        this.#option = option;
        this.#DevToolsActivePortFile = this.#option.userDataDir + '/' +
            'DevToolsActivePort';
        this.#browserProcess = browserProcess;
    }
    private async readPortFromFile() {
        const watcher = Deno.watchFs(this.#DevToolsActivePortFile);
        for await (const event of watcher) {
            if (event.kind == 'modify') {
                watcher.close();
            }
        }
        const content = await Deno.readTextFile(
            this.#DevToolsActivePortFile,
        );
        const wsUrl = 'ws://127.0.0.1:' + content.replaceAll(/\s/g, '');
        this.#ws = new WebSocket(wsUrl);
        this.#ws.onclose = () => {
            console.log('dddd');
        };
    }
    private async readWsUrlFromStderr() {
        const buff = new Uint8Array(100);
        await this.#browserProcess.stderr?.read(buff).then((output) => {
            const wsUrl = String.fromCodePoint(...buff).match(/ws:.+/)?.join();
            console.log(wsUrl);
            if (wsUrl != undefined) {
                this.#ws = new WebSocket(wsUrl);
            }
        });
    }
    public static async launch(execPath: string, option?: Partial<Option>) {
        let userDataDir: string;
        let port: number;

        if (option?.userDataDir == undefined) {
            userDataDir = await Deno.makeTempDir();
        } else {
            userDataDir = option.userDataDir;
        }

        if (option?.port == undefined) {
            port = 0;
            const DevToolsActivePortFile = userDataDir + '/' +
                'DevToolsActivePort';
            await Deno.create(DevToolsActivePortFile).then((fd) => fd.close());
        } else {
            port = option.port;
        }

        const browserProcess = Deno.run({
            cmd: [
                execPath,
                `--headless`,
                `--disable-gpu`,
                `--remote-debugging-port=${port}`,
                `--user-data-dir=${userDataDir}`,
            ],
            stderr: 'piped',
        });

        const ret = new Browser(browserProcess, {
            port: port,
            userDataDir: userDataDir,
        });
        await ret.readWsUrlFromStderr();
        if (ret.#ws == undefined) {
            await ret.readPortFromFile();
        }
        return ret;
    }
    public close() {
        this.#ws?.close();
        this.#browserProcess.stderr?.close();
        this.#browserProcess.close();
    }
}

export interface Option {
    port: number;
    userDataDir: string;
}
